#pragma once

#include <assert.h>

namespace ysimple_system
{
	template<class T> class intrusive_ptr
	{
	private:

		typedef intrusive_ptr this_type;
		
	public:

		typedef T element_type;

		intrusive_ptr() _NOEXCEPT : px( 0 )
		{
		}

		intrusive_ptr( T * p ): px( p )
		{
			if( px != 0 ) px->addRef();
		}

		template<class U>
		intrusive_ptr( intrusive_ptr<U> const & rhs )
			: px( rhs.get() )
		{
			if( px != 0 ) px->addRef();
		}

		intrusive_ptr(intrusive_ptr const & rhs): px( rhs.px )
		{
			if( px != 0 ) px->addRef();
		}
		
		~intrusive_ptr()
		{
			if( px != 0 ) px->release();
		}

		template<class U> intrusive_ptr & operator=(intrusive_ptr<U> const & rhs)
		{
			this_type(rhs).swap(*this);
			return *this;
		}

		intrusive_ptr(intrusive_ptr && rhs) _NOEXCEPT : px( rhs.px )
		{
			rhs.px = 0;
		}

		intrusive_ptr & operator=(intrusive_ptr && rhs) _NOEXCEPT
		{
			this_type( static_cast< intrusive_ptr && >( rhs ) ).swap(*this);
			return *this;
		}

		intrusive_ptr & operator=(intrusive_ptr const & rhs)
		{
			this_type(rhs).swap(*this);
			return *this;
		}

		intrusive_ptr & operator=(T * rhs)
		{
			this_type(rhs).swap(*this);
			return *this;
		}

		void reset() _NOEXCEPT
		{
			this_type().swap( *this );
		}

		void reset( T * rhs )
		{
			this_type( rhs ).swap( *this );
		}

		T * get() const _NOEXCEPT
		{
			return px;
		}

		T & operator*() const
		{
			assert( px != 0 );
			return *px;
		}

		T * operator->() const
		{
			assert( px != 0 );
			return px;
		}
		
		void swap(intrusive_ptr & rhs) _NOEXCEPT
		{
			T * tmp = px;
			px = rhs.px;
			rhs.px = tmp;
		}

	private:

		T * px;
	};

	template<class T, class U> inline bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b)
	{
		return a.get() == b.get();
	}

	template<class T, class U> inline bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b)
	{
		return a.get() != b.get();
	}

	template<class T, class U> inline bool operator==(intrusive_ptr<T> const & a, U * b)
	{
		return a.get() == b;
	}

	template<class T, class U> inline bool operator!=(intrusive_ptr<T> const & a, U * b)
	{
		return a.get() != b;
	}

	template<class T, class U> inline bool operator==(T * a, intrusive_ptr<U> const & b)
	{
		return a == b.get();
	}

	template<class T, class U> inline bool operator!=(T * a, intrusive_ptr<U> const & b)
	{
		return a != b.get();
	}
	
	template<class T> inline bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b)
	{
		return std::less<T *>()(a.get(), b.get());
	}

	template<class T> void swap(intrusive_ptr<T> & lhs, intrusive_ptr<T> & rhs)
	{
		lhs.swap(rhs);
	}

} 

